package net.yla.smartbus.module.ba.right.menu.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.vip.vjtools.vjkit.collection.ListUtil;
import com.vip.vjtools.vjkit.mapper.BeanMapper;
import javassist.bytecode.ByteArray;
import net.yla.smartbus.core.api.RequestCode;
import net.yla.smartbus.core.base.BaseService;
import net.yla.smartbus.core.base.PageBean;
import net.yla.smartbus.core.config.ProjectConstant;
import net.yla.smartbus.core.exception.AppException;
import net.yla.smartbus.core.util.DtoUtils;
import net.yla.smartbus.core.util.PojoJsonUtil;
import net.yla.smartbus.core.util.RedisUtil;
import net.yla.smartbus.core.util.mini.BytesUtils;
import net.yla.smartbus.module.ba.right.menu.dto.MenusTreeDTO;
import net.yla.smartbus.module.ba.right.menu.dto.TaxiMenuDTO;
import net.yla.smartbus.module.ba.right.menu.query.*;
import net.yla.smartbus.module.ba.right.roles.query.QueryOfView;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import net.yla.smartbus.module.ba.right.menu.mapper.MenusMapper;
import net.yla.smartbus.module.ba.right.menu.entity.MenusDO;
import net.yla.smartbus.module.ba.right.menu.service.MenusService;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static org.apache.commons.collections.CollectionUtils.index;

/**
 * @Date: 2020-08-19 17:16
 * @Author: Being Nobody
 * @Version: 1.0
 */
@Service
public class MenusServiceImpl extends BaseService implements MenusService {

    @Autowired
    private MenusMapper menusMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public int deleteByPrimaryKey(Integer menuId) {
        return menusMapper.deleteByPrimaryKey(menuId);
    }

    @Override
    public int insert(MenusDO record) {
        return menusMapper.insert(record);
    }

    @Override
    public int insertSelective(MenusDO record) {
        return menusMapper.insertSelective(record);
    }

    @Override
    public MenusDO selectByPrimaryKey(Integer menuId) {
        return menusMapper.selectByPrimaryKey(menuId);
    }

    @Override
    public int updateByPrimaryKeySelective(MenusDO record) {
        return menusMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public int updateByPrimaryKey(MenusDO record) {
        return menusMapper.updateByPrimaryKey(record);
    }

    /**
     * 获取当前管理菜单
     *
     * @param token
     * @param query
     * @return
     */
    @Override
    public List<MenusTreeDTO> menu(String token, CurrentOfQuery query) {
        List<MenusTreeDTO> menusTree = null;
         Map<Integer, Map<Integer, Integer>> rights = getUserSession(token).getRights();
        if (rights != null) {
            // 1获取菜单所对应的权限
            final Map<Integer, Integer> map = rights.get(ProjectConstant.FUNCTION);
            // 2获取所有菜单
            List<MenusDO> menusList = menusMapper.menuAll(query.getType());
            // 3过滤没有的权限
            List<MenusDO> filterList = menusList.stream().filter(menusDO -> {
                int menuRight = menusDO.getRights();
                int menuId = menusDO.getMenuId();
                int any = menusDO.getAny();
                if (map != null && map.get(menuId) != null) {
                    int hasRight = map.get(menuId);
                    if (any == 0) {
                        return (menuRight & hasRight) == menuRight;
                    } else if (any == 1) {
                        return (menuRight | hasRight) != 0;
                    }
                }
                return false;
            }).collect(Collectors.toList());
            // 4MenusDO >>>MenusTreeDTO
            List<MenusTreeDTO> menusTreeList = this.dispose(filterList);
            // 5生成菜单树
            menusTree = this.generateTree(menusTreeList);
        }

        return menusTree;
    }


    /**
     * 获取指定父级的菜单记录数
     *
     * @param menusOfCount
     * @return
     */
    @Override
    public int menusOfCount(MenusOfCount menusOfCount) {
        // 递归查询
        return menusMapper.count(menusOfCount);
    }

    /**
     * 查看指定菜单的信息记录
     *
     * @param query
     * @return
     */
    @Override
    public List<JSONObject> list(MenusOfQuery query) {
        String sortOrder = super.orderBy(query.getSort(), query.getOrder());
//        PageHelper.offsetPage(query.getOffset(), query.getCount(), sortOrder.isEmpty() ? "menuId" : sortOrder);
        // menuId parentId
        List<MenusDO> list = menusMapper.menusList(query);
        // 封装
        List<JSONObject> result = this.disposeRecord(list, query.getFields());
        return result;
    }

    /**
     * 查看指定菜单的信息记录
     *
     * @param query
     * @return
     */
    @Override
    public JSONObject view(QueryOfView query) {
        if (query.getId() == null) {
            throw new AppException("主键标识为空", RequestCode.Form_Validate_Error);
        }
        // 1.获取指定菜单信息 通过menuId
        MenusDO menusDO = menusMapper.selectByPrimaryKey(query.getId());
        // 2.获取子菜单标识数组
        JSONObject jsonObject = DtoUtils.asDtoJsonForOnly(menusDO);
        // 2.1获取子菜单Id集合
        List<Integer> items = menusMapper.listOfMenuId(query.getId());
        jsonObject.put("items", items);
        return jsonObject;
    }

    /**
     * 添加或更新菜单
     * TODO level 没有用到
     *
     * @param save
     * @return
     */
    @Override
    @Transactional
    public int save(MenusOfSave save) {
        // 添加
        if (save.getId() == null) {
            return this.insertMenu(save);
        }
        // 修改
        else {
            return this.updateMenu(save);
        }
    }

    /**
     * 删除菜单
     *
     * @param query
     * @return
     */
    @Override
    @Transactional
    public int del(QueryOfView query) {
        // 0是否含有这个ID
        MenusDO menusDO = menusMapper.selectByPrimaryKey(query.getId());
        if (menusDO == null) {
            throw new AppException("没有该菜单", RequestCode.Form_Validate_Error);
        }
        // 1获取子孙菜单ID集合
        List<Integer> menusIds = menusMapper.listOfMenuId(query.getId());
        Boolean recursive = query.getRecursive();
        Boolean hasSon = ListUtil.isNotEmpty(menusIds) && menusIds.size() > 0;
        // 2.1 不需要递归 并且 有孩子
        if (!recursive && hasSon) {
            throw new AppException("删除失败", RequestCode.Form_Validate_Error);
        }
        // 2.1刪除当前ID
        if (menusMapper.deleteByPrimaryKey(query.getId()) < 1) {
            throw new AppException("刪除父亲ID失败", RequestCode.Form_Validate_Error);
        }
        // 2.2删除子孙ID集合
        for (Integer menusId : menusIds) {
            if (menusMapper.deleteByPrimaryKey(menusId) < 1) {
                throw new AppException("刪除儿子ID失败", RequestCode.Form_Validate_Error);
            }
        }

        // 3. 移除父亲身上的children含有的相关孩子值
        if (menusDO.getParentId() != 0) {
            // 不是顶级 要删除父亲的children
            // 3.1 获取父亲
            MenusDO menusDOf = menusMapper.selectByPrimaryKey(menusDO.getParentId());
            byte[] children = menusDOf.getChildren();
            if (children != null) {
                // 移除当前菜单ID
                children = BytesUtils.deleteWithValue(children, menusDO.getMenuId());
                // 移除当前菜单孩子ID集合
                for (Integer menusId : menusIds) {
                    children = BytesUtils.deleteWithValue(children, menusId);
                }
            }
            if (menusMapper.updateChildren(children, menusDOf.getMenuId()) < 1) {
                throw new AppException("更新父亲的孩子失败", RequestCode.Form_Validate_Error);
            }
        }
        return 1;
    }

    /**
     * 获取菜单值集合
     *
     * @param ids
     * @return
     */
    @Override
    public List<Integer> menuValue(List<Integer> ids) {
        return menusMapper.selectMenuValue(ids);
    }

    /**
     * 分页查询菜单
     * @param query
     * @return
     */
    @Override
    public PageBean<TaxiMenuDTO> listByQuery(TaxiMenuQuery query) {
        PageHelper.startPage(query.getPageNum(),query.getPageSize(),"menuId DESC");
        List<MenusDO> list = menusMapper.listByQuery(query);
        if (ListUtil.isEmpty(list)) {
            return new PageBean<>(ListUtil.emptyList(),query.getPageNum(),query.getPageSize(),0);
        }
        List<TaxiMenuDTO> dtoList = new ArrayList<>(list.size());
        int count = menusMapper.countByQuery(query);
        for (MenusDO menusDO :list){
            TaxiMenuDTO dto = new TaxiMenuDTO();
            dto.setId(menusDO.getMenuId());
            dto.setName(menusDO.getName());
            MenusDO selectByPrimaryKey = menusMapper.selectByPrimaryKey(menusDO.getParentId());
            if (selectByPrimaryKey!=null){
                String name = selectByPrimaryKey.getName();
                if (name==null){
                    dto.setParentName("--");
                }else {
                    dto.setParentName(name);
                }
            }else {
                dto.setParentName("--");
            }
            dto.setUrl(menusDO.getHref());
            dto.setIcon(menusDO.getTitle());
            dtoList.add(dto);
        }
        return new PageBean<>(dtoList,query.getPageNum(),query.getPageSize(),count);
    }


    /**
     * 插入菜单
     *
     * @param save
     * @return
     */
    private int insertMenu(MenusOfSave save) {
        MenusDO menusDO = this.ToMenusDO(save);
        // 1顶级菜单直接插入
        if (save.getParent() == 0) {
            // 封裝成DO
            menusDO.setQualifiedName(save.getName());
            return this.insertSelective(menusDO);
        }
        // 2子菜单
        MenusDO menusDO1 = menusMapper.selectByParentId(save.getParent());
        if (menusDO1 == null) {
            // 没有这个父亲
            throw new AppException("parenId有误", RequestCode.Form_Validate_Error);
        }

        // 2.2 插入到子菜单
        String qualifiedName = menusDO1.getQualifiedName().concat("/").concat(save.getName());
        menusDO.setQualifiedName(qualifiedName);
        byte[] parents = menusDO1.getParents();
        if (parents == null) {
            parents = new byte[4];
        }
        ByteArray.write32bit(save.getParent(), parents, 0);
        menusDO.setParents(parents);

        if (menusMapper.insertSelective(menusDO) < 1) {
            throw new AppException("插入失败", RequestCode.Form_Validate_Error);
        }
        // 2.2 更新父亲的children
        byte[] children = menusDO1.getChildren();
        if (children == null) {
            children = new byte[4];
        }
        int menuId = menusDO.getMenuId();
        ByteArray.write32bit(menuId, children, 0);
        menusDO1.setChildren(children);
        if (menusMapper.updateByPrimaryKeySelective(menusDO1) < 1) {
            throw new AppException("更新父级失败", RequestCode.Form_Validate_Error);
        }
        // 2.3 更新自己的parents TODO

        return 1;
    }

    private MenusDO ToMenusDO(MenusOfSave save) {
        JSONObject jsonObject = DtoUtils.asDtoJsonForOnly(save);
        MenusDO menusDO = JSONObject.toJavaObject(jsonObject, MenusDO.class);
        return menusDO;
    }

    /**
     * 更新菜单
     *
     * @param save
     * @return
     */
    private int updateMenu(MenusOfSave save) {
        MenusDO menusDOc = menusMapper.selectByPrimaryKey(save.getId());
        if (menusDOc == null) {
            // 没有这个菜单
            throw new AppException("menuId有误", RequestCode.Form_Validate_Error);
        }
        String oldName = menusDOc.getName();
        String newName = save.getName();
        // save---->DO
        JSONObject jsonObject = DtoUtils.asDtoJsonForOnly(save);
        MenusDO menusDOs = JSON.toJavaObject(jsonObject, MenusDO.class);
        // 子菜单集合
        List<Map<String, Object>> subMenus = menusMapper.subMenus(save.getId());

        // parentId 未改变
        if (menusDOc.getParentId() == save.getParent()) {
            // 1). 更新当前菜单名称 菜单全称
            String parentName = this.qualifiedNameOfParent(menusDOc.getQualifiedName(), oldName);
            menusDOs.setQualifiedName(parentName.concat(newName));
            updateByPrimaryKeySelective(menusDOs);
            // 2). 更新子菜单菜单全称
            // 2.2)更新
            for (Map<String, Object> map : subMenus) {
                String qualifiedName = (String) map.get("qualifiedName");
                qualifiedName = this.replaceMenuName(qualifiedName, oldName, newName);
                Integer menuId = (Integer) map.get("menuId");
                if (menusMapper.updateQNameParent(qualifiedName, null, menuId) < 1) {
                    throw new AppException("更新子菜单parent qname失败", RequestCode.Operate_Tip);
                }
            }
        }
        // parentId 发生改变
        else {
            // 修改为顶级菜单
            if (save.getParent() == 0) {
                // 1移除父亲的孩子
                // 1.1 获取父亲
                MenusDO menusDOf = menusMapper.rootByPrimaryKey(save.getId());
                // 1.2 子孙MenuId集合
                List<Integer> menusIds = menusMapper.listOfMenuId(save.getId());
                byte[] children = menusDOf.getChildren();
                if (children != null) {
                    // 移除当前菜单ID
                    children = BytesUtils.deleteWithValue(children, menusDOf.getMenuId());
                    // 移除当前菜单孩子ID集合
                    for (Integer menusId : menusIds) {
                        children = BytesUtils.deleteWithValue(children, menusId);
                    }
                }
                if (menusMapper.updateChildren(children, menusDOf.getMenuId()) < 1) {
                    throw new AppException("更新父亲的孩子失败", RequestCode.Form_Validate_Error);
                }
                // 2修改自己的parentId parents name qualifiedName ...
                String parentName = this.qualifiedNameOfParent(menusDOc.getQualifiedName(), oldName);
                menusDOs.setQualifiedName(parentName.concat(newName));
                menusDOs.setParents(null);
                menusDOs.setLastModifyTime(LocalDateTime.now());
                menusMapper.updateByPrimaryKey1(menusDOs);
                // 3修改孩子的parents qualifiedName
                byte[] parentf = menusDOc.getParents();
                // 3.1 移除孩子中所含parents的
                String qualifiedName = menusDOc.getQualifiedName();
                for (Map<String, Object> map : subMenus) {
                    String qName = (String) map.get("qualifiedName");
                    Integer menuId = (Integer) map.get("menuId");
                    qName = qName.replace(qualifiedName, save.getName());
                    byte[] parents = this.removeParents(parentf, menuId);
                    if (menusMapper.updateQNameParent(qName, parents, menuId) < 1) {
                        throw new AppException("更新子菜单1parent qname失败", RequestCode.Operate_Tip);
                    }
                }


            }
            // 修改为子级菜单
            else {
                // 0). 校验有这个父亲，且这个父亲不是自己的孩子
                MenusDO menusDOp = menusMapper.selectByPrimaryKey(save.getParent());
                if (menusDOp == null) {
                    // 没有这个菜单
                    throw new AppException("parentId有误", RequestCode.Form_Validate_Error);
                }
                // 孩子menuId集合
                List<Integer> menuIdList = menusMapper.listOfMenuId(save.getId());
                if (menuIdList.contains(menusDOp.getMenuId().intValue())) {
                    throw new AppException("你竟然是你父亲的孩子！哈哈", RequestCode.Form_Validate_Error);
                }
                // 1). 父亲加个孩子
                byte[] children = menusDOp.getChildren();
                ByteArray.write16bit(save.getId(), children, children.length);
                menusDOp.setChildren(children);
                if (menusMapper.updateByPrimaryKeySelective(menusDOp) < 1) {
                    throw new AppException("父亲加个孩子失败", RequestCode.Operate_Tip);
                }
                // 2). 当前菜单修改父亲 修改菜单全称
                byte[] parents = menusDOp.getParents();
                String qualifiedName = menusDOp.getQualifiedName();
                ByteArray.write16bit(menusDOp.getMenuId(), parents, parents.length);
                menusDOc.setParents(parents);
                String qqname = qualifiedName.concat("/").concat(newName);
                menusDOc.setQualifiedName(qqname);
                if (menusMapper.updateByPrimaryKeySelective(menusDOc) < 1) {
                    throw new AppException("当前菜单更新失败", RequestCode.Operate_Tip);
                }
                // 3). 当前菜单子菜单修改父亲 修改菜单全称
                ByteArray.write16bit(menusDOc.getMenuId(), parents, parents.length);
                for (Map<String, Object> map : subMenus) {
                    String qName = (String) map.get("qualifiedName");
                    qName = qName.replace(qualifiedName, qqname);
                    Integer menuId = (Integer) map.get("menuId");
                    if (menusMapper.updateQNameParent(qName, parents, menuId) < 1) {
                        throw new AppException("更新子菜单parent qname失败", RequestCode.Operate_Tip);
                    }
                }
            }

        }

        return 1;
    }

    private byte[] removeParents(byte[] parentf, Integer menuId) {
        MenusDO menusDO = menusMapper.selectByPrimaryKey(menuId);
        byte[] parents = menusDO.getParents();
        if (parentf != null) {
            for (int i = 0; i < parentf.length; i++) {
                parents = BytesUtils.deleteWithValue(parents, parents[i]);
            }
        }
        return parents;
    }

    private String replaceMenuName(String qualifiedName, String oldName, String newName) {
        String[] split = qualifiedName.split("/");
        for (int i = 0; i < split.length; i++) {
            if (split[i].equals(oldName)) {
                split[i] = newName;
                break;
            }
        }
        return StringUtils.join(split, "/");
    }

    private String qualifiedNameOfParent(String qualifiedName, String name) {
        if (StringUtils.isNotEmpty(qualifiedName)) {
            return qualifiedName.replace(name, "");
        }

        return qualifiedName;
    }

    private List<JSONObject> disposeRecord(List<MenusDO> list, List<String> fields) {
        List<JSONObject> objectList = null;
        if (ListUtil.isNotEmpty(list) && list.size() > 0) {
            objectList = new ArrayList<>();
            for (MenusDO menusDO : list) {
                JSONObject jsonObject = PojoJsonUtil.writeJsonByField(menusDO, fields);
                //获取子菜单Id集合
                List<Integer> items = menusMapper.listOfMenuId(menusDO.getMenuId());
                jsonObject.put("items", items);
                objectList.add(jsonObject);
            }
        }
        JSON.parseObject(JSON.toJSONString(objectList, SerializerFeature.DisableCircularReferenceDetect));

        return objectList;
    }

    private List<MenusTreeDTO> dispose(List<MenusDO> filterList) {
        List<MenusTreeDTO> result = null;
        if (filterList != null && !filterList.isEmpty() && filterList.size() > 0) {
            result = new ArrayList<>(filterList.size());
            for (MenusDO menusDO : filterList) {
                try {
                    MenusTreeDTO dto = BeanMapper.map(menusDO, MenusTreeDTO.class);
                    dto.setId(menusDO.getMenuId());
                    result.add(dto);
                }catch (Exception e){
                   throw new AppException("请退出重新登录！",RequestCode.Form_Validate_Error);
                }
            }
        }
        return result;
    }

    /**
     * 生成菜单树
     *
     * @param filterList
     * @return
     */
    private List<MenusTreeDTO> generateTree(List<MenusTreeDTO> filterList) {
        List<MenusTreeDTO> result = null;
        // 获取所有的父级菜单
        if (filterList != null && filterList.size() > 0) {
            List<MenusTreeDTO> parentMenus = filterList.stream().filter(menusTreeDTO -> menusTreeDTO.getParentId() == 0).collect(Collectors.toList());
            result = getTree(parentMenus, filterList);
        }
        return result;
    }

    public List<MenusTreeDTO> getTree(List<MenusTreeDTO> rootList, List<MenusTreeDTO> bodyList) {
        if (bodyList != null && !bodyList.isEmpty()) {
            // 声明一个map，用来过滤已操作过的数据
            Map<String, String> map = Maps.newHashMapWithExpectedSize(bodyList.size());
            rootList.forEach(beanTree -> getChild(bodyList, beanTree, map));
            return rootList;
        }
        return null;
    }

    public void getChild(List<MenusTreeDTO> bodyList, MenusTreeDTO beanTree, Map<String, String> map) {
        List<MenusTreeDTO> childList = Lists.newArrayList();
        bodyList.stream().filter(c -> !map.containsKey(c.getId())).filter(c -> c.getParentId().equals(beanTree.getId()))
                .forEach(c -> {
                    map.put(c.getId().toString(), c.getParentId().toString());
                    getChild(bodyList, c, map);
                    childList.add(c);
                });
        beanTree.setItems(childList);
    }

    /**
     * @param list
     * @return
     */
    public static List<MenusDO> parseMenuTree(List<MenusDO> list) {
        List<MenusDO> result = new ArrayList<MenusDO>();
        // 1、获取第一级节点
        for (MenusDO menu : list) {
            if (menu.getParentId() == 0) {
                result.add(menu);
            }
        }
        // 2、递归获取子节点
        for (MenusDO parent : result) {
            parent = recursiveTree(parent, list);
        }

        return result;
    }

    public static MenusDO recursiveTree(MenusDO parent, List<MenusDO> list) {
        for (MenusDO menu : list) {
            if (Objects.equals(parent.getMenuId(), menu.getMenuId())) {
                menu = recursiveTree(menu, list);
            }
        }

        return parent;
    }


    public static void main(String[] args) {
        /*List<MenusDO> list = new ArrayList<MenusDO>();
        //TODO 这里从数据库获取全量菜单后放到list中
        //树形结构数据生成*/
//        List<MenusDO> result = parseMenuTree(menusList);
//        System.out.println(JSONObject.toJSONString(result));

    }
}
